<?php
declare (strict_types = 1);

namespace app\command;

use fast\Http;
use app\library\Redis;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
use think\facade\Cache;
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Timer;
use Workerman\Worker;

/**
 * 作为客户端，与抖音建立 WSS 连接
 * */
class LiveListen extends Command
{
    protected $redis = null;

    protected function configure()
    {
        // 指令配置
        $this->setName('LiveListen')
            ->addArgument('key', Argument::REQUIRED, '键')
            ->addArgument('status', Argument::OPTIONAL, '状态')
            ->setDescription('the LiveListen command');
    }

    protected function execute(Input $input, Output $output)
    {
        // 初始化参数
        $this->redis = new Redis(Cache::getStoreConfig('redis'));
        $cache_key = trim($input->getArgument('key'));
        $room = $this->redis->get($cache_key);
        if (!$room) {
            exit('键错误或不存在');
        }
        // 根据直播类型选择不同方案
        switch ($room['type']) {
            case 'douyin':
                $this->douyin($room);
                break;
            case 'kuaishou':
                $this->kuaishou($room);
                break;
            case 'shipinghao':
                break;
            default:
                exit('直播类型错误');
        }
    }

    /**
     * 与抖音建立WebSocket连接
     * @param array $room 键名
     * */
    private function douyin($room)
    {
        // 重构argv参数，以守护进程启动workerman服务
        global $argv;
        $argv[] = 'start';
        $argv[] = '-d';
        // 设置pid文件路径
        $worker = new Worker();
        $worker->name = $room['key'];
        if (!is_dir(__DIR__ . '/../../runtime/pids')) {
            mkdir(__DIR__ . '/../../runtime/pids');
        }
        Worker::$pidFile = __DIR__ . "/../../runtime/pids/{$room['key']}.pid"; // 设置pid文件，开启多进程
        // 进程启动时
        $worker->onWorkerStart = function () use ($room) {
            // 以websocket协议连接远程websocket服务器 ws://IP:端口
            $ws_url = "ws://webcast5-ws-web-hl.douyin.com/webcast/im/push/v2/?app_name=douyin_web&version_code=180800&webcast_sdk_version=1.0.10&update_version_code=1.0.10&compress=gzip&device_platform=web&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Mozilla&browser_version=5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/118.0.0.0%20Safari/537.36%20Edg/118.0.2088.69&browser_online=true&tz_name=Asia/Shanghai&cursor=r-1_d-1_u-1_fh-7294507099622085669_t-1698384834280&internal_ext=internal_src:dim|wss_push_room_id:".$room['room_id']."|wss_push_did:7293456606909236748|dim_log_id:202310271333541DC3CE7F472C391171F5|first_req_ms:1698384834190|fetch_time:1698384834280|seq:1|wss_info:0-1698384834281-0-0|wrds_kvs:WebcastRoomStatsMessage-1698384825265668451_WebcastInRoomBannerMessage-GrowthCommonBannerSubSyncKey-1698381333404075821_HighlightContainerSyncData-16_InputPanelComponentSyncData-1698371798814602649_AudienceGiftSyncData-1698384804163809534_WebcastRoomRankMessage-1698384051244334807&host=https://live.douyin.com&aid=6383&live_id=1&did_rule=3&endpoint=live_pc&support_wrds=1&user_unique_id=7293456606909236748&im_path=/webcast/im/fetch/&identity=audience&need_persist_msg_count=15&room_id=".$room['room_id']."&heartbeatDuration=0&signature=".$room['extend']['signature'];
//            $ws_url = "ws://webcast5-ws-web-lf.douyin.com/webcast/im/push/v2/?app_name=douyin_web&version_code=180800&webcast_sdk_version=1.0.12&update_version_code=1.0.12&compress=gzip&device_platform=web&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Mozilla&browser_version=5.0 (Windows)&browser_online=true&tz_name=Asia/Shanghai&cursor=t-1699329042841_r-1_d-1_u-1_h-7298562397870855218&internal_ext=internal_src:dim|wss_push_room_id:".$room['room_id']."|wss_push_did:7295965355581982245|dim_log_id:20231107115042507BFE0D99E24A9B7ADA|first_req_ms:1699329042751|fetch_time:1699329042841|seq:1|wss_info:0-1699329042841-0-0|wrds_kvs:WebcastRoomStreamAdaptationMessage-1699329041827787123_InputPanelComponentSyncData-1699305367660627364_HighlightContainerSyncData-55_WebcastRoomRankMessage-1699329039549692539_WebcastRoomStatsMessage-1699329039495063256&host=https://live.douyin.com&aid=6383&live_id=1&did_rule=3&endpoint=live_pc&support_wrds=1&user_unique_id=7295965355581982245&im_path=/webcast/im/fetch/&identity=audience&need_persist_msg_count=15&room_id=".$room['room_id']."&heartbeatDuration=0&signature=".$room['extend']['signature'];
            $ws_connection = new AsyncTcpConnection($ws_url);
            $ws_connection->transport = 'ssl';
            // 下面cookie可能会过期，需要定时更换，在浏览器Cookie中找ttwid值替换即可
            $ws_connection->headers = [
                'cookie' => 'ttwid=1|SG2n_Af2DQiznXN5XoN0ctiIjorhPZgD-webswXKh_w|1698895579|19fd93a4240802109b9dd2628cdedfb93514fab5380098f245ca723cec07a018',
                'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76',
            ];

            // 连接成功
            $ws_connection->onConnect = function ($connection) use ($room) {
                // 设置定时器，每隔10秒回复一次消息，防止断开连接
                $connection->timer_id = Timer::add(10, function () use ($connection, $room){
                    $connection->send('');
                    // 检测键是否过期，过期时关闭定时器并停止进程
                    if (!$this->redis->has($room['key'])) {
                        // 结束进程
                        if (is_file(__DIR__ . "/../../runtime/pids/{$room['key']}.pid")) {
                            $pid = file_get_contents(__DIR__ . "/../../runtime/pids/{$room['key']}.pid");
                            if ($pid) {
                                unlink(__DIR__ . "/../../runtime/pids/{$room['key']}.pid");
                                exec("kill -9 $pid");
                            }
                            // 删除队列
                            $this->redis->delete($room['list_key']);
                        }
                        // 删除定时器，停止服务
                        Timer::del($connection->timer_id);
                        Worker::stopAll();
                    }
                });
            };

            // 远程websocket服务器发来消息时
            $ws_connection->onMessage = function ($connection, $data) use ($room) {
                if ($this->redis->has($room['key'])) {
                    $data = \app\library\NodeJs::decode_dy_data($data);
                    foreach ($data as $message) {
                        // 数据入栈，重置键过期时间
                        $this->redis->lpush($room['list_key'], json_encode([
                            'type'      => $message['type'],
                            'user'      => $message['user'],
                            'content'   => $message['content'],
                            'timestamp' => time()
                        ]));
                        echo (isset($message['nickname']) ? $message['nickname'] . ":" : "") . $message['content'] . PHP_EOL;
                    }
                    $this->redis->expire($room['list_key'], $room['expire']);
                }
            };

            // 连接上发生错误时，一般是连接远程websocket服务器失败错误
            $ws_connection->onError = function ($connection, $code, $msg) {
                echo "error: $msg\n";
            };

            // 当连接远程websocket服务器的连接断开时
            $ws_connection->onClose = function ($connection) {
                echo "connection closed\n";
                Timer::del($connection->timer_id); // 删除定时器
            };

            // 设置好以上各种回调后，执行连接操作
            $ws_connection->connect();
        };
        // 执行
        Worker::runAll();
    }

    /**
     * 轮训快手弹幕接口
     * */
    private function kuaishou($room)
    {
        // 初始化参数
        $url = 'http://livev.m.chenzhongtech.com/wap/live/feed';
        $params = [
            'liveStreamId'  => $room['room_id'],
            'cursor'        => $room['extend']['cursor'],
        ];
        $options = [
            CURLOPT_HTTPHEADER => [
                "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
                "Cookie: _did=web_5191125033E5E1F9; did=web_b958aa90415d6e488b4220785b4dee87ae88; didv=1698912686000; userId=3677664303",
                "Referer: https://live.kuaishou.com",
            ]
        ];
        // 开始轮训
        $result = true;
        while ($result) {
            if ($this->redis->has($room['key'])) {
                $data = json_decode(json_decode(Http::get($url, $params, $options), true), true);
                if (isset($data['result']) && $data['result'] == 1) {
                    $params['cursor'] = $data['cursor'];
                    foreach ($data['liveStreamFeeds'] as $message) {
                        // 数据入栈，重置键过期时间
                        $this->redis->lpush($room['list_key'], json_encode([
                            'type'      => $message['type'],
                            'user'      => $message['author'],
                            'content'   => $message['content'],
                            'timestamp' => (int)($message['time'] / 1000)
                        ]));
                        echo $message['content'] .PHP_EOL;
                    }
                    $this->redis->expire($room['list_key'], $room['expire']);
                }
                sleep(1);
            } else {
                $result = false;
                $this->redis->delete($room['list_key']);
            }
        }
    }

    /**
     * 轮训美团弹幕接口
     * */
    public function meituan()
    {

    }
}